package com.cn.ycy.filter;

import com.cn.ycy.filter.entity.CachedBodyOutputMessage;
import com.cn.ycy.filter.entity.DefaultServerRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

@Component
public class SelfModifyRequestBody2GatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

  @Override
  public GatewayFilter apply(NameValueConfig config) {
    return new RequestHandlerFilter();
  }

  class RequestHandlerFilter implements GatewayFilter {

    private final Logger logger = LoggerFactory.getLogger(SelfModifyRequestBodyGatewayFilterFactory.RequestHandlerFilter.class);

    private final List<HttpMessageReader<?>> messageReaders;

    public RequestHandlerFilter() {
      this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
    }

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      return chain.filter(exchange.mutate().request(decorate(exchange)).build());
    }

    private ServerHttpRequestDecorator decorate(ServerWebExchange exchange) {
      logger.info(exchange.getRequest().getMethodValue());
      ServerRequest serverRequest = new DefaultServerRequest(exchange, this.messageReaders);
      Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(requestBody -> {
        logger.info("requestBody {}", requestBody);
        //解密
        return Mono.just("<xml><ToUserName><![CDATA[wxf5c6ef6ab8b3d357]]></ToUserName>\n" +
                "<FromUserName><![CDATA[ofUnX6Svlbp2IDQxT4a41oBMTbbY]]></FromUserName>\n" +
                "<CreateTime>1610353518</CreateTime>\n" +
                "<MsgType><![CDATA[text]]></MsgType>\n" +
                "<Content><![CDATA[一点毛病没有]]></Content>\n" +
                "<MsgId>23054722249185575</MsgId>\n" +
                "</xml>");
      });
      BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
      HttpHeaders headers = new HttpHeaders();
      headers.putAll(exchange.getRequest().getHeaders());

      // the new content type will be computed by bodyInserter
      // and then set in the request decorator
      headers.remove(HttpHeaders.CONTENT_LENGTH);
      CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
      ServerHttpRequestDecorator decorate = decorate(exchange, headers, outputMessage);
      return decorate;
    }


    private ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
      return new ServerHttpRequestDecorator(exchange.getRequest()) {
        @Override
        public HttpHeaders getHeaders() {
          long contentLength = headers.getContentLength();
          HttpHeaders httpHeaders = new HttpHeaders();
          httpHeaders.putAll(super.getHeaders());
          if (contentLength > 0) {
            httpHeaders.setContentLength(contentLength);
          } else {
            // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
            // httpbin.org
            httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
          }
          return httpHeaders;
        }

        @Override
        public Flux<DataBuffer> getBody() {
          return outputMessage.getBody();
        }
      };
    }


  }

}



